{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "Copy of MovieLens Project.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "collapsed_sections": [
        "VLyHpP9GcNI8",
        "ZecYlb32aPRG",
        "7Gnr6yC9VqxW",
        "jYBWeAsAbPS3",
        "3Kaamk135WaW",
        "IiuFHrce044O",
        "WwawVY_MeCDi",
        "SdbppJj0seHC",
        "PHwhGmC5YtGj",
        "EfLvsj9ax5Bp",
        "9CMimleIm8xp",
        "wqAAa_8JvpVi",
        "kbW3AkTRxKfO",
        "ZdpQw7tNxB23",
        "NlyBl-d00vbS",
        "vbBp9pYqy_C8",
        "WoOWizoNHuWC",
        "gta8xQuwg0_l",
        "2Z4dkFEMIT0i",
        "aKbSMddyMfs7",
        "YJl1ooI9_MfH",
        "Nb7OxcMvmFpU",
        "HRZ_GFV1mFpU",
        "EvIXZS7b6KBx",
        "4a40rehOQdb1",
        "zV0mnIBb40H6",
        "V9ZRdSVBITP1",
        "mzWcIK2Donh-",
        "jXXbIrp8oniV",
        "RPN0p-gZBmHv"
      ],
      "machine_shape": "hm",
      "include_colab_link": true
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.3"
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/dean-sh/Movie-Ratings-Collaborating-Filltering/blob/master/Embeddings%20(Fast%20AI%2C%20Keras).ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AI8iUPuAgW34",
        "colab_type": "text"
      },
      "source": [
        "MovieLens Recommendations - Embeddings\n",
        "=============================================\n",
        "## Dean Shabi, Dedi Kovatch, July 2019\n",
        "## Final Project for TCDS - Technion Data Science Specialization\n",
        "\n",
        "MovieLens data sets were collected by the GroupLens Research Project\n",
        "at the University of Minnesota.\n",
        " \n",
        "This data set consists of: \n",
        "\t* 100,000 ratings (1-5) from 943 users on 1682 movies. \n",
        "\t* Each user has rated at least 20 movies. \n",
        "        * Simple demographic info for the users (age, gender, occupation, zip)\n",
        "\n",
        "The data was collected through the MovieLens web site\n",
        "(movielens.umn.edu) during the seven-month period from September 19th, \n",
        "1997 through April 22nd, 1998. This data has been cleaned up - users\n",
        "who had less than 20 ratings or did not have complete demographic\n",
        "information were removed from this data set. Detailed descriptions of\n",
        "the data file can be found at the end of this file.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "VLyHpP9GcNI8",
        "colab_type": "text"
      },
      "source": [
        "## Data Description\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "79B5aQBncSJn",
        "colab_type": "text"
      },
      "source": [
        "\n",
        "Here are brief descriptions of the data.\n",
        "\n",
        "ml-data.tar.gz   -- Compressed tar file.  To rebuild the u data files do this:\n",
        "                gunzip ml-data.tar.gz\n",
        "                tar xvf ml-data.tar\n",
        "                mku.sh\n",
        "\n",
        "u.data     -- The full u data set, 100000 ratings by 943 users on 1682 items.\n",
        "              Each user has rated at least 20 movies.  Users and items are\n",
        "              numbered consecutively from 1.  The data is randomly\n",
        "              ordered. This is a tab separated list of \n",
        "\t         user id | item id | rating | timestamp. \n",
        "              The time stamps are unix seconds since 1/1/1970 UTC   \n",
        "\n",
        "u.info     -- The number of users, items, and ratings in the u data set.\n",
        "\n",
        "u.item     -- Information about the items (movies); this is a tab separated\n",
        "              list of\n",
        "              movie id | movie title | release date | video release date |\n",
        "              IMDb URL | unknown | Action | Adventure | Animation |\n",
        "              Children's | Comedy | Crime | Documentary | Drama | Fantasy |\n",
        "              Film-Noir | Horror | Musical | Mystery | Romance | Sci-Fi |\n",
        "              Thriller | War | Western |\n",
        "              The last 19 fields are the genres, a 1 indicates the movie\n",
        "              is of that genre, a 0 indicates it is not; movies can be in\n",
        "              several genres at once.\n",
        "              The movie ids are the ones used in the u.data data set.\n",
        "\n",
        "u.genre    -- A list of the genres.\n",
        "\n",
        "u.user     -- Demographic information about the users; this is a tab\n",
        "              separated list of\n",
        "              user id | age | gender | occupation | zip code\n",
        "              The user ids are the ones used in the u.data data set.\n",
        "\n",
        "u.occupation -- A list of the occupations.\n",
        "\n",
        "u1.base    -- The data sets u1.base and u1.test through u5.base and u5.test\n",
        "u1.test       are 80%/20% splits of the u data into training and test data.\n",
        "u2.base       Each of u1, ..., u5 have disjoint test sets; this if for\n",
        "u2.test       5 fold cross validation (where you repeat your experiment\n",
        "u3.base       with each training and test set and average the results).\n",
        "u3.test       These data sets can be generated from u.data by mku.sh.\n",
        "u4.base\n",
        "u4.test\n",
        "u5.base\n",
        "u5.test\n",
        "\n",
        "ua.base    -- The data sets ua.base, ua.test, ub.base, and ub.test\n",
        "ua.test       split the u data into a training set and a test set with\n",
        "ub.base       exactly 10 ratings per user in the test set.  The sets\n",
        "ub.test       ua.test and ub.test are disjoint.  These data sets can\n",
        "              be generated from u.data by mku.sh.\n",
        "\n",
        "allbut.pl  -- The script that generates training and test sets where\n",
        "              all but n of a users ratings are in the training data.\n",
        "\n",
        "mku.sh     -- A shell script to generate all the u data sets from u.data."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZecYlb32aPRG",
        "colab_type": "text"
      },
      "source": [
        "## Imports\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "eSXwQhcbxpTA",
        "colab": {}
      },
      "source": [
        "import numpy as np\n",
        "import pandas as pd\n",
        "import collections\n",
        "import seaborn as sns\n",
        "%matplotlib inline\n",
        "# from mpl_toolkits.mplot3d import Axes3D\n",
        "from IPython import display\n",
        "from matplotlib import pyplot as plt\n",
        "import sklearn\n",
        "import sklearn.manifold\n",
        "# import tensorflow as tf\n",
        "# tf.logging.set_verbosity(tf.logging.ERROR)\n",
        "\n",
        "# # Add some convenience functions to Pandas DataFrame.\n",
        "# pd.options.display.max_rows = 10\n",
        "# pd.options.display.float_format = '{:.3f}'.format\n",
        "\n",
        "\n",
        "\n",
        "# # Install Altair and activate its colab renderer.\n",
        "# print(\"Installing Altair...\")\n",
        "# !pip install git+git://github.com/altair-viz/altair.git\n",
        "# import altair as alt\n",
        "# alt.data_transformers.enable('default', max_rows=None)\n",
        "# alt.renderers.enable('colab')\n",
        "# print(\"Done installing Altair.\")\n",
        "\n",
        "# # Install spreadsheets and import authentication module.\n",
        "# USER_RATINGS = False\n",
        "# !pip install --upgrade -q gspread\n",
        "# from google.colab import auth\n",
        "# import gspread\n",
        "# from oauth2client.client import GoogleCredentials"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "7Gnr6yC9VqxW",
        "colab_type": "text"
      },
      "source": [
        "## **Importing dataset, preprocessing**\n",
        "\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "gq9LYL4ef_Ms",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# download the MovieLens Data, and create DataFrames containing movies, users, and ratings.\n",
        "\n",
        "print(\"Downloading movielens data...\")\n",
        "import zipfile\n",
        "import urllib.request\n",
        "\n",
        "urllib.request.urlretrieve(\"http://files.grouplens.org/datasets/movielens/ml-100k.zip\", \"movielens.zip\")\n",
        "zip_ref = zipfile.ZipFile('movielens.zip', \"r\")\n",
        "zip_ref.extractall()\n",
        "print(\"Done. Dataset contains:\")\n",
        "print(zip_ref.read('ml-100k/u.info'))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "Ny6Op3qUe3bc",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Load each data set (users, movies, and ratings).\n",
        "users_cols = ['user_id', 'age', 'sex', 'occupation', 'zip_code']\n",
        "users = pd.read_csv(\n",
        "    'ml-100k/u.user', sep='|', names=users_cols, encoding='latin-1')\n",
        "\n",
        "ratings_cols = ['user_id', 'movie_id', 'rating', 'unix_timestamp']\n",
        "ratings = pd.read_csv(\n",
        "    'ml-100k/u.data', sep='\\t', names=ratings_cols, encoding='latin-1')\n",
        "\n",
        "# The movies file contains a binary feature for each genre.\n",
        "genre_cols = [\n",
        "    \"genre_unknown\", \"Action\", \"Adventure\", \"Animation\", \"Children\", \"Comedy\",\n",
        "    \"Crime\", \"Documentary\", \"Drama\", \"Fantasy\", \"Film-Noir\", \"Horror\",\n",
        "    \"Musical\", \"Mystery\", \"Romance\", \"Sci-Fi\", \"Thriller\", \"War\", \"Western\"\n",
        "]\n",
        "movies_cols = [\n",
        "    'movie_id', 'title', 'release_date', \"video_release_date\", \"imdb_url\"] + genre_cols\n",
        "\n",
        "movies = pd.read_csv(\n",
        "    'ml-100k/u.item', sep='|', names=movies_cols, encoding='latin-1')\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dXNpqJsDgeqj",
        "colab_type": "text"
      },
      "source": [
        "Some Preproccessing"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "xjlkpHekgXyq",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Since the ids start at 1, we shift them to start at 0.\n",
        "users[\"user_id\"] = users[\"user_id\"].apply(lambda x: str(x-1))\n",
        "movies[\"movie_id\"] = movies[\"movie_id\"].apply(lambda x: str(x-1))\n",
        "movies[\"year\"] = movies['release_date'].apply(lambda x: str(x).split('-')[-1])\n",
        "ratings[\"movie_id\"] = ratings[\"movie_id\"].apply(lambda x: str(x-1))\n",
        "ratings[\"user_id\"] = ratings[\"user_id\"].apply(lambda x: str(x-1))\n",
        "ratings[\"rating\"] = ratings[\"rating\"].apply(lambda x: float(x))\n",
        "\n",
        "# Compute the number of movies to which a genre is assigned.\n",
        "genre_occurences = movies[genre_cols].sum().to_dict()\n",
        "\n",
        "# Since some movies can belong to more than one genre, we create different\n",
        "# 'genre' columns as follows:\n",
        "# - all_genres: all the active genres of the movie.\n",
        "# - genre: randomly sampled from the active genres.\n",
        "def mark_genres(movies, genres):\n",
        "    def get_random_genre(gs):\n",
        "        active = [genre for genre, g in zip(genres, gs) if g==1]\n",
        "        if len(active) == 0:\n",
        "            return 'Other'\n",
        "        return np.random.choice(active)\n",
        "    def get_all_genres(gs):\n",
        "        active = [genre for genre, g in zip(genres, gs) if g==1]\n",
        "        if len(active) == 0:\n",
        "            return 'Other'\n",
        "        return '-'.join(active)\n",
        "    movies['genre'] = [\n",
        "        get_random_genre(gs) for gs in zip(*[movies[genre] for genre in genres])]\n",
        "    movies['all_genres'] = [\n",
        "        get_all_genres(gs) for gs in zip(*[movies[genre] for genre in genres])]\n",
        "\n",
        "mark_genres(movies, genre_cols)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "apgLczpJgWcf",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Create one merged DataFrame containing all the movielens data.\n",
        "movielens = ratings.merge(movies, on='movie_id').merge(users, on='user_id')\n",
        "\n",
        "# Utility to split the data into training and test sets.\n",
        "def split_dataframe(df, holdout_fraction=0.1):\n",
        "    \"\"\"Splits a DataFrame into training and test sets.\n",
        "    Args:\n",
        "    df: a dataframe.\n",
        "    holdout_fraction: fraction of dataframe rows to use in the test set.\n",
        "    Returns:\n",
        "    train: dataframe for training\n",
        "    test: dataframe for testing\n",
        "    \"\"\"\n",
        "    test = df.sample(frac=holdout_fraction, replace=False)\n",
        "    train = df[~df.index.isin(test.index)]\n",
        "    return train, test"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "XJMhuEXXgXdB",
        "colab_type": "code",
        "outputId": "4e49f141-5aa3-4bf6-84f2-68c491c77716",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "movies.head()"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>movie_id</th>\n",
              "      <th>title</th>\n",
              "      <th>release_date</th>\n",
              "      <th>video_release_date</th>\n",
              "      <th>imdb_url</th>\n",
              "      <th>genre_unknown</th>\n",
              "      <th>Action</th>\n",
              "      <th>Adventure</th>\n",
              "      <th>Animation</th>\n",
              "      <th>Children</th>\n",
              "      <th>Comedy</th>\n",
              "      <th>Crime</th>\n",
              "      <th>Documentary</th>\n",
              "      <th>Drama</th>\n",
              "      <th>Fantasy</th>\n",
              "      <th>Film-Noir</th>\n",
              "      <th>Horror</th>\n",
              "      <th>Musical</th>\n",
              "      <th>Mystery</th>\n",
              "      <th>Romance</th>\n",
              "      <th>Sci-Fi</th>\n",
              "      <th>Thriller</th>\n",
              "      <th>War</th>\n",
              "      <th>Western</th>\n",
              "      <th>year</th>\n",
              "      <th>genre</th>\n",
              "      <th>all_genres</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>0</td>\n",
              "      <td>Toy Story (1995)</td>\n",
              "      <td>01-Jan-1995</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?Toy%20Story%2...</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>1</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1995</td>\n",
              "      <td>Comedy</td>\n",
              "      <td>Animation-Children-Comedy</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>1</td>\n",
              "      <td>GoldenEye (1995)</td>\n",
              "      <td>01-Jan-1995</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?GoldenEye%20(...</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1995</td>\n",
              "      <td>Adventure</td>\n",
              "      <td>Action-Adventure-Thriller</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>2</td>\n",
              "      <td>Four Rooms (1995)</td>\n",
              "      <td>01-Jan-1995</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?Four%20Rooms%...</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1995</td>\n",
              "      <td>Thriller</td>\n",
              "      <td>Thriller</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>3</td>\n",
              "      <td>Get Shorty (1995)</td>\n",
              "      <td>01-Jan-1995</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?Get%20Shorty%...</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1995</td>\n",
              "      <td>Comedy</td>\n",
              "      <td>Action-Comedy-Drama</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>4</td>\n",
              "      <td>Copycat (1995)</td>\n",
              "      <td>01-Jan-1995</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?Copycat%20(1995)</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1995</td>\n",
              "      <td>Crime</td>\n",
              "      <td>Crime-Drama-Thriller</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "  movie_id              title  ...      genre                 all_genres\n",
              "0        0   Toy Story (1995)  ...     Comedy  Animation-Children-Comedy\n",
              "1        1   GoldenEye (1995)  ...  Adventure  Action-Adventure-Thriller\n",
              "2        2  Four Rooms (1995)  ...   Thriller                   Thriller\n",
              "3        3  Get Shorty (1995)  ...     Comedy        Action-Comedy-Drama\n",
              "4        4     Copycat (1995)  ...      Crime       Crime-Drama-Thriller\n",
              "\n",
              "[5 rows x 27 columns]"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 6
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "MgCRtomqgXHp",
        "colab_type": "code",
        "outputId": "f890e6f0-1681-42fb-cd98-4417a0abb50a",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "users.head()"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>user_id</th>\n",
              "      <th>age</th>\n",
              "      <th>sex</th>\n",
              "      <th>occupation</th>\n",
              "      <th>zip_code</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>0</td>\n",
              "      <td>24</td>\n",
              "      <td>M</td>\n",
              "      <td>technician</td>\n",
              "      <td>85711</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>1</td>\n",
              "      <td>53</td>\n",
              "      <td>F</td>\n",
              "      <td>other</td>\n",
              "      <td>94043</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>2</td>\n",
              "      <td>23</td>\n",
              "      <td>M</td>\n",
              "      <td>writer</td>\n",
              "      <td>32067</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>3</td>\n",
              "      <td>24</td>\n",
              "      <td>M</td>\n",
              "      <td>technician</td>\n",
              "      <td>43537</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>4</td>\n",
              "      <td>33</td>\n",
              "      <td>F</td>\n",
              "      <td>other</td>\n",
              "      <td>15213</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "  user_id  age sex  occupation zip_code\n",
              "0       0   24   M  technician    85711\n",
              "1       1   53   F       other    94043\n",
              "2       2   23   M      writer    32067\n",
              "3       3   24   M  technician    43537\n",
              "4       4   33   F       other    15213"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 7
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "UHVdwsCBgWyl",
        "colab_type": "code",
        "outputId": "eb7bac9f-29b5-467c-ba41-c41603ccd7a6",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "ratings.head()"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>user_id</th>\n",
              "      <th>movie_id</th>\n",
              "      <th>rating</th>\n",
              "      <th>unix_timestamp</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>195</td>\n",
              "      <td>241</td>\n",
              "      <td>3.0</td>\n",
              "      <td>881250949</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>185</td>\n",
              "      <td>301</td>\n",
              "      <td>3.0</td>\n",
              "      <td>891717742</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>21</td>\n",
              "      <td>376</td>\n",
              "      <td>1.0</td>\n",
              "      <td>878887116</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>243</td>\n",
              "      <td>50</td>\n",
              "      <td>2.0</td>\n",
              "      <td>880606923</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>165</td>\n",
              "      <td>345</td>\n",
              "      <td>1.0</td>\n",
              "      <td>886397596</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "  user_id movie_id  rating  unix_timestamp\n",
              "0     195      241     3.0       881250949\n",
              "1     185      301     3.0       891717742\n",
              "2      21      376     1.0       878887116\n",
              "3     243       50     2.0       880606923\n",
              "4     165      345     1.0       886397596"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 8
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "SlgCWxb9h3Er",
        "colab_type": "code",
        "outputId": "9d57673f-c3e4-4bce-d051-da98d1149c46",
        "colab": {
          "base_uri": "https://localhost:8080/"
        }
      },
      "source": [
        "movielens.head()"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>user_id</th>\n",
              "      <th>movie_id</th>\n",
              "      <th>rating</th>\n",
              "      <th>unix_timestamp</th>\n",
              "      <th>title</th>\n",
              "      <th>release_date</th>\n",
              "      <th>video_release_date</th>\n",
              "      <th>imdb_url</th>\n",
              "      <th>genre_unknown</th>\n",
              "      <th>Action</th>\n",
              "      <th>Adventure</th>\n",
              "      <th>Animation</th>\n",
              "      <th>Children</th>\n",
              "      <th>Comedy</th>\n",
              "      <th>Crime</th>\n",
              "      <th>Documentary</th>\n",
              "      <th>Drama</th>\n",
              "      <th>Fantasy</th>\n",
              "      <th>Film-Noir</th>\n",
              "      <th>Horror</th>\n",
              "      <th>Musical</th>\n",
              "      <th>Mystery</th>\n",
              "      <th>Romance</th>\n",
              "      <th>Sci-Fi</th>\n",
              "      <th>Thriller</th>\n",
              "      <th>War</th>\n",
              "      <th>Western</th>\n",
              "      <th>year</th>\n",
              "      <th>genre</th>\n",
              "      <th>all_genres</th>\n",
              "      <th>age</th>\n",
              "      <th>sex</th>\n",
              "      <th>occupation</th>\n",
              "      <th>zip_code</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>195</td>\n",
              "      <td>241</td>\n",
              "      <td>3.0</td>\n",
              "      <td>881250949</td>\n",
              "      <td>Kolya (1996)</td>\n",
              "      <td>24-Jan-1997</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?Kolya%20(1996)</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1997</td>\n",
              "      <td>Comedy</td>\n",
              "      <td>Comedy</td>\n",
              "      <td>49</td>\n",
              "      <td>M</td>\n",
              "      <td>writer</td>\n",
              "      <td>55105</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>195</td>\n",
              "      <td>256</td>\n",
              "      <td>2.0</td>\n",
              "      <td>881251577</td>\n",
              "      <td>Men in Black (1997)</td>\n",
              "      <td>04-Jul-1997</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?Men+in+Black+...</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1997</td>\n",
              "      <td>Sci-Fi</td>\n",
              "      <td>Action-Adventure-Comedy-Sci-Fi</td>\n",
              "      <td>49</td>\n",
              "      <td>M</td>\n",
              "      <td>writer</td>\n",
              "      <td>55105</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>195</td>\n",
              "      <td>110</td>\n",
              "      <td>4.0</td>\n",
              "      <td>881251793</td>\n",
              "      <td>Truth About Cats &amp; Dogs, The (1996)</td>\n",
              "      <td>26-Apr-1996</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?Truth%20About...</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1996</td>\n",
              "      <td>Comedy</td>\n",
              "      <td>Comedy-Romance</td>\n",
              "      <td>49</td>\n",
              "      <td>M</td>\n",
              "      <td>writer</td>\n",
              "      <td>55105</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>3</th>\n",
              "      <td>195</td>\n",
              "      <td>24</td>\n",
              "      <td>4.0</td>\n",
              "      <td>881251955</td>\n",
              "      <td>Birdcage, The (1996)</td>\n",
              "      <td>08-Mar-1996</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?Birdcage,%20T...</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1996</td>\n",
              "      <td>Comedy</td>\n",
              "      <td>Comedy</td>\n",
              "      <td>49</td>\n",
              "      <td>M</td>\n",
              "      <td>writer</td>\n",
              "      <td>55105</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>4</th>\n",
              "      <td>195</td>\n",
              "      <td>381</td>\n",
              "      <td>4.0</td>\n",
              "      <td>881251843</td>\n",
              "      <td>Adventures of Priscilla, Queen of the Desert, ...</td>\n",
              "      <td>01-Jan-1994</td>\n",
              "      <td>NaN</td>\n",
              "      <td>http://us.imdb.com/M/title-exact?Adventures%20...</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>0</td>\n",
              "      <td>1994</td>\n",
              "      <td>Comedy</td>\n",
              "      <td>Comedy-Drama</td>\n",
              "      <td>49</td>\n",
              "      <td>M</td>\n",
              "      <td>writer</td>\n",
              "      <td>55105</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "  user_id movie_id  rating  unix_timestamp  ... age sex  occupation zip_code\n",
              "0     195      241     3.0       881250949  ...  49   M      writer    55105\n",
              "1     195      256     2.0       881251577  ...  49   M      writer    55105\n",
              "2     195      110     4.0       881251793  ...  49   M      writer    55105\n",
              "3     195       24     4.0       881251955  ...  49   M      writer    55105\n",
              "4     195      381     4.0       881251843  ...  49   M      writer    55105\n",
              "\n",
              "[5 rows x 34 columns]"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 9
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "2Z4dkFEMIT0i",
        "colab_type": "text"
      },
      "source": [
        "# FastAI Embeddings model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "uHrWk76oGoTL",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# !pip install fastai\n",
        "import pandas as pd\n",
        "from fastai.collab import CollabDataBunch, collab_learner, EmbeddingNN\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "mpWk1tb8IY2O",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "data = CollabDataBunch.from_df(ratings, valid_pct=0.15)\n",
        "data.show_batch()\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "rOT1Z5xGMcbN",
        "colab_type": "text"
      },
      "source": [
        "\n",
        "## EmbeddingDotBias Model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "QB9Zg8UVIma5",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "learn = collab_learner(data, n_factors=50, y_range=(0.,5.), wd=.1)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "OhZFACmVIrTK",
        "colab_type": "code",
        "outputId": "79bd8f1a-5819-4a3f-c0da-16a1d2fc5881",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 300
        }
      },
      "source": [
        "learn.lr_find()\n",
        "learn.recorder.plot(skip_end=15)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              ""
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "LR Finder is complete, type {learner_name}.recorder.plot() to see the graph.\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "mnE2ERllItkt",
        "colab_type": "code",
        "outputId": "782aaadd-9a9e-462b-a06e-1d00db419edf",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 196
        }
      },
      "source": [
        "learn.fit_one_cycle(5, 5e-3, wd=0.1)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: left;\">\n",
              "      <th>epoch</th>\n",
              "      <th>train_loss</th>\n",
              "      <th>valid_loss</th>\n",
              "      <th>time</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <td>0</td>\n",
              "      <td>0.963994</td>\n",
              "      <td>0.956801</td>\n",
              "      <td>00:08</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>1</td>\n",
              "      <td>0.868230</td>\n",
              "      <td>0.883104</td>\n",
              "      <td>00:08</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>2</td>\n",
              "      <td>0.793230</td>\n",
              "      <td>0.830477</td>\n",
              "      <td>00:08</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>3</td>\n",
              "      <td>0.669829</td>\n",
              "      <td>0.811959</td>\n",
              "      <td>00:09</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>4</td>\n",
              "      <td>0.576130</td>\n",
              "      <td>0.809654</td>\n",
              "      <td>00:08</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>"
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "wuzKozvTI3Pc",
        "colab_type": "code",
        "outputId": "f7bdd0ba-a23c-4418-82c9-d46831713672",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 301
        }
      },
      "source": [
        "(users, items), ratings = next(iter(data.valid_dl))\n",
        "preds = learn.model(users, items)\n",
        "print('Real\\tPred\\tDifference')\n",
        "for p in list(zip(ratings, preds))[:16]:\n",
        "    print('{}\\t{:.1f}\\t{:.1f}'.format(p[0],p[1],p[1]-p[0]))\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Real\tPred\tDifference\n",
            "3.0\t2.2\t-0.8\n",
            "4.0\t2.9\t-1.1\n",
            "2.0\t2.8\t0.8\n",
            "3.0\t2.9\t-0.1\n",
            "5.0\t4.3\t-0.7\n",
            "3.0\t3.7\t0.7\n",
            "1.0\t2.4\t1.4\n",
            "5.0\t4.3\t-0.7\n",
            "4.0\t3.4\t-0.6\n",
            "1.0\t2.9\t1.9\n",
            "4.0\t4.2\t0.2\n",
            "5.0\t4.3\t-0.7\n",
            "5.0\t3.7\t-1.3\n",
            "2.0\t2.9\t0.9\n",
            "2.0\t3.1\t1.1\n",
            "2.0\t3.7\t1.7\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "aKbSMddyMfs7",
        "colab_type": "text"
      },
      "source": [
        "##EmbeddingNN  Model"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "6W7ApTLaJxKA",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "learn = collab_learner(data, use_nn=True, emb_szs={'user_id': 40, 'movie_id':40}, layers=[256, 128], y_range=(1, 5))\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "qxBCi4P1MrKW",
        "colab_type": "code",
        "outputId": "a45f6aa7-1aff-40ca-d487-8948bd3017df",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 300
        }
      },
      "source": [
        "learn.lr_find() # find learning rate\n",
        "learn.recorder.plot() # plot learning rate graph\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              ""
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "LR Finder is complete, type {learner_name}.recorder.plot() to see the graph.\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "nkN3BclyMtbn",
        "colab_type": "code",
        "outputId": "427543e1-94dd-4fe7-f93a-39d00458ae25",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 196
        }
      },
      "source": [
        "learn.fit_one_cycle(5, 1e-2)\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: left;\">\n",
              "      <th>epoch</th>\n",
              "      <th>train_loss</th>\n",
              "      <th>valid_loss</th>\n",
              "      <th>time</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <td>0</td>\n",
              "      <td>0.996246</td>\n",
              "      <td>0.963006</td>\n",
              "      <td>00:11</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>1</td>\n",
              "      <td>0.914653</td>\n",
              "      <td>0.900042</td>\n",
              "      <td>00:11</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>2</td>\n",
              "      <td>0.883064</td>\n",
              "      <td>0.878848</td>\n",
              "      <td>00:11</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>3</td>\n",
              "      <td>0.818831</td>\n",
              "      <td>0.861804</td>\n",
              "      <td>00:11</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <td>4</td>\n",
              "      <td>0.784336</td>\n",
              "      <td>0.864615</td>\n",
              "      <td>00:11</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>"
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "kE5UTye3NXF4",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "learn.save('movies-dot-1')\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YJl1ooI9_MfH",
        "colab_type": "text"
      },
      "source": [
        "# Recommendation using Keras Embeddings\n",
        "\n",
        "We used a NN based Matrix Factorization method, where the matrices are embeddings learned my the model.\n",
        "The final layer is a dot product as usual in MF.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "lj2JYe-7RTAC",
        "colab": {}
      },
      "source": [
        "# Load each data set (users, movies, and ratings).\n",
        "ratings_cols = ['user_id', 'movie_id', 'rating', 'unix_timestamp']\n",
        "\n",
        "train_df = pd.read_csv(\n",
        "    'ml-100k/u1.base', sep='\\t', names=ratings_cols, encoding='latin-1')\n",
        "\n",
        "test_df = pd.read_csv(\n",
        "    'ml-100k/u1.test', sep='\\t', names=ratings_cols, encoding='latin-1')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "akHxdktURTAH",
        "colab": {}
      },
      "source": [
        "pivot_train = train_df.pivot_table(values = 'rating', index = 'movie_id', columns = 'user_id')\n",
        "pivot_train.fillna(0, inplace = True)\n",
        "\n",
        "pivot_test = test_df.pivot_table(values = 'rating', index = 'movie_id', columns = 'user_id')\n",
        "pivot_test.fillna(0, inplace = True)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "J5aVMw296MaP",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# https://nipunbatra.github.io/blog/2017/recommend-keras.html"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "b6x7rryk_ZaH",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "from sklearn.model_selection import train_test_split\n",
        "train, test = train_test_split(train_df[['user_id','movie_id','rating']], test_size=0.2)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "sRuDnkSmRetz",
        "colab_type": "code",
        "outputId": "847f894f-c685-414c-854b-caeb6f092f16",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 33
        }
      },
      "source": [
        "train.shape, test.shape"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "((64000, 3), (16000, 3))"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 16
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "QVvvEKmG_iS-",
        "colab_type": "code",
        "outputId": "6c424186-d8f6-4fdf-99a9-c0ce954f8ebb",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 33
        }
      },
      "source": [
        "#Matrix factorisation in Keras\n",
        "import keras\n",
        "from IPython.display import SVG\n",
        "from keras.optimizers import Adam\n",
        "from keras.utils.vis_utils import model_to_dot\n",
        "n_users, n_movies = len(movielens.user_id.unique()), len(movielens.movie_id.unique())\n",
        "from keras.regularizers import l2, l1\n",
        "\n",
        "n_latent_factors = 30"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Using TensorFlow backend.\n"
          ],
          "name": "stderr"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qcWV6nJ2_pwq",
        "colab_type": "text"
      },
      "source": [
        "**Dot architecture - Multi layer Embeddings**\n",
        "\n",
        "Dedi - I added L1 regularization, because we were overfitting pretty hard.\n",
        "L1 instead of L2, because L2 tends to make weights go to 0, and we will use the meaning of the embedding that way."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "eVkx9huvgjeX",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "movie_input = keras.layers.Input(shape=[1],name='Item')\n",
        "movie_embedding = keras.layers.Embedding(n_movies + 1, n_latent_factors, name='Movie-Embedding', \n",
        "                                         embeddings_regularizer=l1(1e-6))(movie_input)\n",
        "movie_vec = keras.layers.Flatten(name='FlattenMovies')(movie_embedding)\n",
        "\n",
        "user_input = keras.layers.Input(shape=[1],name='User')\n",
        "user_vec = keras.layers.Flatten(name='FlattenUsers')(keras.layers.Embedding(n_users + 1,\n",
        "                                                                            n_latent_factors,name='User-Embedding',\n",
        "                                                                           embeddings_regularizer=l1(1e-6))(user_input))\n",
        "\n",
        "prod = keras.layers.dot([movie_vec, user_vec], axes=1, normalize=False) #original command from article: prod = keras.layers.merge([movie_vec, user_vec], mode='dot',name='DotProduct')\n",
        "model = keras.Model([user_input, movie_input], prod)\n",
        "model.compile(keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, epsilon=None, decay=0.0001, amsgrad=False), 'mae', metrics=['mse'])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "LefOfXgqLspb",
        "colab_type": "code",
        "outputId": "52c0e0ad-6f17-412d-b7fa-1e6e5cb17fbe",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 426
        }
      },
      "source": [
        "#model visualization\n",
        "SVG(model_to_dot(model,  show_shapes=True, show_layer_names=True, rankdir='HB').create(prog='dot', format='svg'))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<IPython.core.display.SVG object>"
            ],
            "image/svg+xml": "<svg height=\"304pt\" viewBox=\"0.00 0.00 722.50 304.00\" width=\"723pt\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n<g class=\"graph\" id=\"graph0\" transform=\"scale(1 1) rotate(0) translate(4 300)\">\n<title>G</title>\n<polygon fill=\"#ffffff\" points=\"-4,4 -4,-300 718.5,-300 718.5,4 -4,4\" stroke=\"transparent\"/>\n<!-- 140208606664296 -->\n<g class=\"node\" id=\"node1\">\n<title>140208606664296</title>\n<polygon fill=\"none\" points=\"55.5,-249.5 55.5,-295.5 298.5,-295.5 298.5,-249.5 55.5,-249.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"112\" y=\"-268.8\">Item: InputLayer</text>\n<polyline fill=\"none\" points=\"168.5,-249.5 168.5,-295.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"197.5\" y=\"-280.3\">input:</text>\n<polyline fill=\"none\" points=\"168.5,-272.5 226.5,-272.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"197.5\" y=\"-257.3\">output:</text>\n<polyline fill=\"none\" points=\"226.5,-249.5 226.5,-295.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"262.5\" y=\"-280.3\">(None, 1)</text>\n<polyline fill=\"none\" points=\"226.5,-272.5 298.5,-272.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"262.5\" y=\"-257.3\">(None, 1)</text>\n</g>\n<!-- 140208606664240 -->\n<g class=\"node\" id=\"node3\">\n<title>140208606664240</title>\n<polygon fill=\"none\" points=\"0,-166.5 0,-212.5 354,-212.5 354,-166.5 0,-166.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"100.5\" y=\"-185.8\">Movie-Embedding: Embedding</text>\n<polyline fill=\"none\" points=\"201,-166.5 201,-212.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"230\" y=\"-197.3\">input:</text>\n<polyline fill=\"none\" points=\"201,-189.5 259,-189.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"230\" y=\"-174.3\">output:</text>\n<polyline fill=\"none\" points=\"259,-166.5 259,-212.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"306.5\" y=\"-197.3\">(None, 1)</text>\n<polyline fill=\"none\" points=\"259,-189.5 354,-189.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"306.5\" y=\"-174.3\">(None, 1, 30)</text>\n</g>\n<!-- 140208606664296&#45;&gt;140208606664240 -->\n<g class=\"edge\" id=\"edge1\">\n<title>140208606664296-&gt;140208606664240</title>\n<path d=\"M177,-249.3799C177,-241.1745 177,-231.7679 177,-222.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"180.5001,-222.784 177,-212.784 173.5001,-222.784 180.5001,-222.784\" stroke=\"#000000\"/>\n</g>\n<!-- 140208606664856 -->\n<g class=\"node\" id=\"node2\">\n<title>140208606664856</title>\n<polygon fill=\"none\" points=\"421,-249.5 421,-295.5 665,-295.5 665,-249.5 421,-249.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"478\" y=\"-268.8\">User: InputLayer</text>\n<polyline fill=\"none\" points=\"535,-249.5 535,-295.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"564\" y=\"-280.3\">input:</text>\n<polyline fill=\"none\" points=\"535,-272.5 593,-272.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"564\" y=\"-257.3\">output:</text>\n<polyline fill=\"none\" points=\"593,-249.5 593,-295.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"629\" y=\"-280.3\">(None, 1)</text>\n<polyline fill=\"none\" points=\"593,-272.5 665,-272.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"629\" y=\"-257.3\">(None, 1)</text>\n</g>\n<!-- 140208606664744 -->\n<g class=\"node\" id=\"node4\">\n<title>140208606664744</title>\n<polygon fill=\"none\" points=\"371.5,-166.5 371.5,-212.5 714.5,-212.5 714.5,-166.5 371.5,-166.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"466.5\" y=\"-185.8\">User-Embedding: Embedding</text>\n<polyline fill=\"none\" points=\"561.5,-166.5 561.5,-212.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"590.5\" y=\"-197.3\">input:</text>\n<polyline fill=\"none\" points=\"561.5,-189.5 619.5,-189.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"590.5\" y=\"-174.3\">output:</text>\n<polyline fill=\"none\" points=\"619.5,-166.5 619.5,-212.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"667\" y=\"-197.3\">(None, 1)</text>\n<polyline fill=\"none\" points=\"619.5,-189.5 714.5,-189.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"667\" y=\"-174.3\">(None, 1, 30)</text>\n</g>\n<!-- 140208606664856&#45;&gt;140208606664744 -->\n<g class=\"edge\" id=\"edge2\">\n<title>140208606664856-&gt;140208606664744</title>\n<path d=\"M543,-249.3799C543,-241.1745 543,-231.7679 543,-222.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"546.5001,-222.784 543,-212.784 539.5001,-222.784 546.5001,-222.784\" stroke=\"#000000\"/>\n</g>\n<!-- 140208606665360 -->\n<g class=\"node\" id=\"node5\">\n<title>140208606665360</title>\n<polygon fill=\"none\" points=\"55,-83.5 55,-129.5 353,-129.5 353,-83.5 55,-83.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"127.5\" y=\"-102.8\">FlattenMovies: Flatten</text>\n<polyline fill=\"none\" points=\"200,-83.5 200,-129.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"229\" y=\"-114.3\">input:</text>\n<polyline fill=\"none\" points=\"200,-106.5 258,-106.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"229\" y=\"-91.3\">output:</text>\n<polyline fill=\"none\" points=\"258,-83.5 258,-129.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305.5\" y=\"-114.3\">(None, 1, 30)</text>\n<polyline fill=\"none\" points=\"258,-106.5 353,-106.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"305.5\" y=\"-91.3\">(None, 30)</text>\n</g>\n<!-- 140208606664240&#45;&gt;140208606665360 -->\n<g class=\"edge\" id=\"edge3\">\n<title>140208606664240-&gt;140208606665360</title>\n<path d=\"M184.521,-166.3799C187.2482,-157.9962 190.3834,-148.3584 193.3302,-139.2996\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"196.6605,-140.3762 196.4257,-129.784 190.0038,-138.2108 196.6605,-140.3762\" stroke=\"#000000\"/>\n</g>\n<!-- 140208606665024 -->\n<g class=\"node\" id=\"node6\">\n<title>140208606665024</title>\n<polygon fill=\"none\" points=\"385.5,-83.5 385.5,-129.5 672.5,-129.5 672.5,-83.5 385.5,-83.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"452.5\" y=\"-102.8\">FlattenUsers: Flatten</text>\n<polyline fill=\"none\" points=\"519.5,-83.5 519.5,-129.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"548.5\" y=\"-114.3\">input:</text>\n<polyline fill=\"none\" points=\"519.5,-106.5 577.5,-106.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"548.5\" y=\"-91.3\">output:</text>\n<polyline fill=\"none\" points=\"577.5,-83.5 577.5,-129.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"625\" y=\"-114.3\">(None, 1, 30)</text>\n<polyline fill=\"none\" points=\"577.5,-106.5 672.5,-106.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"625\" y=\"-91.3\">(None, 30)</text>\n</g>\n<!-- 140208606664744&#45;&gt;140208606665024 -->\n<g class=\"edge\" id=\"edge4\">\n<title>140208606664744-&gt;140208606665024</title>\n<path d=\"M539.1002,-166.3799C537.7162,-158.1745 536.1295,-148.7679 534.6301,-139.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"538.042,-139.0625 532.9274,-129.784 531.1395,-140.2269 538.042,-139.0625\" stroke=\"#000000\"/>\n</g>\n<!-- 140208606664576 -->\n<g class=\"node\" id=\"node7\">\n<title>140208606664576</title>\n<polygon fill=\"none\" points=\"210.5,-.5 210.5,-46.5 507.5,-46.5 507.5,-.5 210.5,-.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"250.5\" y=\"-19.8\">dot_1: Dot</text>\n<polyline fill=\"none\" points=\"290.5,-.5 290.5,-46.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"319.5\" y=\"-31.3\">input:</text>\n<polyline fill=\"none\" points=\"290.5,-23.5 348.5,-23.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"319.5\" y=\"-8.3\">output:</text>\n<polyline fill=\"none\" points=\"348.5,-.5 348.5,-46.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"428\" y=\"-31.3\">[(None, 30), (None, 30)]</text>\n<polyline fill=\"none\" points=\"348.5,-23.5 507.5,-23.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"428\" y=\"-8.3\">(None, 1)</text>\n</g>\n<!-- 140208606665360&#45;&gt;140208606664576 -->\n<g class=\"edge\" id=\"edge5\">\n<title>140208606665360-&gt;140208606664576</title>\n<path d=\"M247.1761,-83.3799C265.658,-73.4832 287.4034,-61.8388 306.7809,-51.4625\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"308.6234,-54.4461 315.7868,-46.6399 305.3189,-48.2752 308.6234,-54.4461\" stroke=\"#000000\"/>\n</g>\n<!-- 140208606665024&#45;&gt;140208606664576 -->\n<g class=\"edge\" id=\"edge6\">\n<title>140208606665024-&gt;140208606664576</title>\n<path d=\"M481.6455,-83.3799C461.1,-73.3488 436.8771,-61.5224 415.4085,-51.0406\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"416.9168,-47.8822 406.3951,-46.6399 413.8456,-54.1725 416.9168,-47.8822\" stroke=\"#000000\"/>\n</g>\n</g>\n</svg>"
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 19
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "MB3b6RuiL32i",
        "colab_type": "code",
        "outputId": "1a617650-7e59-4315-86dc-881491b73033",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 385
        }
      },
      "source": [
        "    model.summary()"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "__________________________________________________________________________________________________\n",
            "Layer (type)                    Output Shape         Param #     Connected to                     \n",
            "==================================================================================================\n",
            "Item (InputLayer)               (None, 1)            0                                            \n",
            "__________________________________________________________________________________________________\n",
            "User (InputLayer)               (None, 1)            0                                            \n",
            "__________________________________________________________________________________________________\n",
            "Movie-Embedding (Embedding)     (None, 1, 30)        50490       Item[0][0]                       \n",
            "__________________________________________________________________________________________________\n",
            "User-Embedding (Embedding)      (None, 1, 30)        28320       User[0][0]                       \n",
            "__________________________________________________________________________________________________\n",
            "FlattenMovies (Flatten)         (None, 30)           0           Movie-Embedding[0][0]            \n",
            "__________________________________________________________________________________________________\n",
            "FlattenUsers (Flatten)          (None, 30)           0           User-Embedding[0][0]             \n",
            "__________________________________________________________________________________________________\n",
            "dot_1 (Dot)                     (None, 1)            0           FlattenMovies[0][0]              \n",
            "                                                                 FlattenUsers[0][0]               \n",
            "==================================================================================================\n",
            "Total params: 78,810\n",
            "Trainable params: 78,810\n",
            "Non-trainable params: 0\n",
            "__________________________________________________________________________________________________\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "efkuXNcHAU3i",
        "colab_type": "text"
      },
      "source": [
        " **TODO** \n",
        "\n",
        "\n",
        "1.   Add CV instead of test-train split\n",
        "2.   Try different hyperparameters\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "KiMxF7GJ_2NQ",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "history = model.fit([train.user_id, train.movie_id], train.rating, epochs=80, batch_size = 128,\n",
        "                    validation_split=0.2, verbose=1)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "f7qbSwDoHYks",
        "colab_type": "text"
      },
      "source": [
        "**With Regularization:**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "zH4bjmV0Hdom",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "#train loss vs. epochs \n",
        "pd.Series(history.history['loss']).plot(logy=True, label = \"Train Loss\")\n",
        "pd.Series(history.history['val_loss']).plot(logy=True, color='green', label = \"Val Loss\")\n",
        "\n",
        "plt.title(\"Train Loss vs Val Loss - with L1 regularization\")\n",
        "plt.xlabel(\"Epoch\")\n",
        "plt.ylabel(\"Loss (Log)\")\n",
        "plt.legend()\n",
        "\n",
        "y_hat = np.round(model.predict([test.user_id, test.movie_id]),0)\n",
        "y_true = test.rating\n",
        "\n",
        "from sklearn.metrics import mean_absolute_error\n",
        "mean_absolute_error(y_true, y_hat)\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qDuJ523BHVR_",
        "colab_type": "text"
      },
      "source": [
        "**Without Regularization:**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "p9RXfmzJMkE9",
        "colab_type": "code",
        "outputId": "40727b4b-81fb-4b09-c6a1-442442ff96a1",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 312
        }
      },
      "source": [
        "#train loss vs. epochs \n",
        "pd.Series(history.history['loss']).plot(logy=True, label = \"Train Loss\")\n",
        "pd.Series(history.history['val_loss']).plot(logy=True, color='green', label = \"Val Loss\")\n",
        "\n",
        "plt.title(\"Train Loss vs Val Loss\")\n",
        "plt.xlabel(\"Epoch\")\n",
        "plt.ylabel(\"Loss (Log)\")\n",
        "plt.legend()\n",
        "\n",
        "print(\"This is a clear overfit :) \\n\")"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "This is a clear overfit :)\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\neOCBB2hqaqKnp2dKyzNjzmI6Hc6554Dncqre8d8bO1SDEJEzY/Xq1SxZsoRzzz2XefPmceGFF05q\nf9/97nd5+umnY/e3bNnC3/3d3/Ge97wH5xxXX301f/RHf8S2bdu46667cM5hZnzpS18iGAxyyy23\n0NnZSTgc5rOf/Sz5+fmTPcQhzDk3pTs8kwJzz3Uf+NtHefjO8xNdFBGZhF27drF48eJEFyMlxfvb\nmtlW59zaUV4Sk5RNTFGZPqNRfRAiItMiqQMiw5emgBARmSZJHxDtvQP09E/uDAERERkpyQMicqVC\n1SJERKZeUgZE9DTXE32RU1wb2xQQIiJTLSkDwjn3nHPu7qKCyCldh9s1FkJEZKolZUBEZfgixdfl\nNkRkMi699NIRg96+8pWv8PGPf3zM1+Xlxb8O3Gjbk01SB4QZlOZl0qgahIhMws0338yGDRuGbNuw\nYQM33xz3akFnjaQOCIDygF+d1CIyKTfccAPPP/98bHKgAwcOcPjwYS6++GK6urp473vfy+rVq1m+\nfDk/+clPJvQeBw4c4LLLLmPFihW8973v5eDBgwA89dRTLFu2jJUrV/Lud78bgDfffJPzzz+fVatW\nsWLFCnbv3j01B3qakvJSG4NVBLI52Dy11x8RkcT51L99iu1Htk/pPleVr+IrV3xl1MeLi4s5//zz\neeGFF7j22mvZsGEDN910E2aG3+/nRz/6EQUFBTQ1NbFu3Tquueaa057v+U//9E+54447uOOOO3jo\noYe45557+PGPf8x9993Hxo0bqaqqil2y+5vf/Caf/OQnufXWW+nv7ycUSsy8N0lfg6gM+NVJLSKT\nNriZaXDzknOOL3zhC6xYsYLLL7+choYGjh49etr7f+WVV7jllsjUN7fffju/+tWvALjwwgv5yEc+\nwre//e1YEKxfv54vfvGLfOlLX6Kuro7s7OypOMTTlvQ1iPJANp19QbpOBMnLSvrDETnrjfVLfzpd\ne+21fPrTn2bbtm309PSwZs0aAB5//HGOHz/O1q1bycjIoKamJu4lvifqm9/8Jps2beL5559nzZo1\nbN26lVtuuYXa2lqef/55rrrqKr71rW9x2WWXTdl7jldS1iCi4yDa29upLPQDcES1CBGZhLy8PC69\n9FI++tGPDumcbm9vZ9asWWRkZPDiiy9SV1c3of1fcMEFsRrK448/zsUXXwzA3r17qa2t5b777qOs\n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\nrkJggg==\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "CDs7ufbSlDNb",
        "colab_type": "code",
        "outputId": "3772aa51-b6bb-4532-bbe3-77f9f820bcd2",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 34
        }
      },
      "source": [
        "y_hat = np.round(model.predict([test.user_id, test.movie_id]),0)\n",
        "y_true = test.rating\n",
        "\n",
        "from sklearn.metrics import mean_absolute_error\n",
        "mean_absolute_error(y_true, y_hat)\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "0.7715"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 31
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mgayDCObAsWK",
        "colab_type": "text"
      },
      "source": [
        "**Embeddings->NN Architecture**"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "_H_u8NuimfsJ",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "n_latent_factors_user = 5\n",
        "n_latent_factors_movie = 8\n",
        "\n",
        "movie_input = keras.layers.Input(shape=[1],name='Movie')\n",
        "movie_embedding = keras.layers.Embedding(n_movies + 1, n_latent_factors_movie, name='Movie-Embedding')(movie_input)\n",
        "movie_vec = keras.layers.Flatten(name='FlattenMovies')(movie_embedding)\n",
        "movie_vec = keras.layers.Dropout(0.012)(movie_vec)\n",
        "\n",
        "\n",
        "user_input = keras.layers.Input(shape=[1],name='User')\n",
        "user_vec = keras.layers.Flatten(name='FlattenUsers')(keras.layers.Embedding(n_users + 1, n_latent_factors_user,\n",
        "                                                                            name='User-Embedding')(user_input))\n",
        "user_vec = keras.layers.Dropout(0.02)(user_vec)\n",
        "\n",
        "\n",
        "concat = keras.layers.concatenate([movie_vec, user_vec], axis= 1, name='Concat')\n",
        "\n",
        "\n",
        "concat_dropout = keras.layers.Dropout(0.2)(concat)\n",
        "dense = keras.layers.Dense(200,name='FullyConnected')(concat)\n",
        "dropout_1 = keras.layers.Dropout(0.2,name='Dropout')(dense)\n",
        "dense_2 = keras.layers.Dense(100,name='FullyConnected-1')(concat)\n",
        "dropout_2 = keras.layers.Dropout(0.2,name='Dropout')(dense_2)\n",
        "dense_3 = keras.layers.Dense(50,name='FullyConnected-2')(dense_2)\n",
        "dropout_3 = keras.layers.Dropout(0.2,name='Dropout')(dense_3)\n",
        "dense_4 = keras.layers.Dense(20,name='FullyConnected-3', activation='relu')(dense_3)\n",
        "\n",
        "\n",
        "result = keras.layers.Dense(1, activation='relu',name='Activation')(dense_4)\n",
        "adam = Adam()\n",
        "model = keras.Model([user_input, movie_input], result)\n",
        "model.compile(optimizer=adam,loss= 'mean_squared_error')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "ojUaomrdKCjm",
        "colab_type": "code",
        "outputId": "12a21444-b653-4f8e-98ab-a0d120a90dff",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 979
        }
      },
      "source": [
        "#model visualization\n",
        "SVG(model_to_dot(model,  show_shapes=True, show_layer_names=True, rankdir='HB').create(prog='dot', format='svg'))"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "<IPython.core.display.SVG object>"
            ],
            "image/svg+xml": "<svg height=\"719pt\" viewBox=\"0.00 0.00 707.50 719.00\" width=\"708pt\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n<g class=\"graph\" id=\"graph0\" transform=\"scale(1 1) rotate(0) translate(4 715)\">\n<title>G</title>\n<polygon fill=\"#ffffff\" points=\"-4,4 -4,-715 703.5,-715 703.5,4 -4,4\" stroke=\"transparent\"/>\n<!-- 139950899142216 -->\n<g class=\"node\" id=\"node1\">\n<title>139950899142216</title>\n<polygon fill=\"none\" points=\"45.5,-664.5 45.5,-710.5 300.5,-710.5 300.5,-664.5 45.5,-664.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"108\" y=\"-683.8\">Movie: InputLayer</text>\n<polyline fill=\"none\" points=\"170.5,-664.5 170.5,-710.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"199.5\" y=\"-695.3\">input:</text>\n<polyline fill=\"none\" points=\"170.5,-687.5 228.5,-687.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"199.5\" y=\"-672.3\">output:</text>\n<polyline fill=\"none\" points=\"228.5,-664.5 228.5,-710.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"264.5\" y=\"-695.3\">(None, 1)</text>\n<polyline fill=\"none\" points=\"228.5,-687.5 300.5,-687.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"264.5\" y=\"-672.3\">(None, 1)</text>\n</g>\n<!-- 139950899141376 -->\n<g class=\"node\" id=\"node3\">\n<title>139950899141376</title>\n<polygon fill=\"none\" points=\"0,-581.5 0,-627.5 346,-627.5 346,-581.5 0,-581.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"100.5\" y=\"-600.8\">Movie-Embedding: Embedding</text>\n<polyline fill=\"none\" points=\"201,-581.5 201,-627.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"230\" y=\"-612.3\">input:</text>\n<polyline fill=\"none\" points=\"201,-604.5 259,-604.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"230\" y=\"-589.3\">output:</text>\n<polyline fill=\"none\" points=\"259,-581.5 259,-627.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302.5\" y=\"-612.3\">(None, 1)</text>\n<polyline fill=\"none\" points=\"259,-604.5 346,-604.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302.5\" y=\"-589.3\">(None, 1, 8)</text>\n</g>\n<!-- 139950899142216&#45;&gt;139950899141376 -->\n<g class=\"edge\" id=\"edge1\">\n<title>139950899142216-&gt;139950899141376</title>\n<path d=\"M173,-664.3799C173,-656.1745 173,-646.7679 173,-637.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"176.5001,-637.784 173,-627.784 169.5001,-637.784 176.5001,-637.784\" stroke=\"#000000\"/>\n</g>\n<!-- 139950898901232 -->\n<g class=\"node\" id=\"node2\">\n<title>139950898901232</title>\n<polygon fill=\"none\" points=\"410,-664.5 410,-710.5 654,-710.5 654,-664.5 410,-664.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"467\" y=\"-683.8\">User: InputLayer</text>\n<polyline fill=\"none\" points=\"524,-664.5 524,-710.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"553\" y=\"-695.3\">input:</text>\n<polyline fill=\"none\" points=\"524,-687.5 582,-687.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"553\" y=\"-672.3\">output:</text>\n<polyline fill=\"none\" points=\"582,-664.5 582,-710.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"618\" y=\"-695.3\">(None, 1)</text>\n<polyline fill=\"none\" points=\"582,-687.5 654,-687.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"618\" y=\"-672.3\">(None, 1)</text>\n</g>\n<!-- 139950899021808 -->\n<g class=\"node\" id=\"node4\">\n<title>139950899021808</title>\n<polygon fill=\"none\" points=\"364.5,-581.5 364.5,-627.5 699.5,-627.5 699.5,-581.5 364.5,-581.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"459.5\" y=\"-600.8\">User-Embedding: Embedding</text>\n<polyline fill=\"none\" points=\"554.5,-581.5 554.5,-627.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"583.5\" y=\"-612.3\">input:</text>\n<polyline fill=\"none\" points=\"554.5,-604.5 612.5,-604.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"583.5\" y=\"-589.3\">output:</text>\n<polyline fill=\"none\" points=\"612.5,-581.5 612.5,-627.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"656\" y=\"-612.3\">(None, 1)</text>\n<polyline fill=\"none\" points=\"612.5,-604.5 699.5,-604.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"656\" y=\"-589.3\">(None, 1, 5)</text>\n</g>\n<!-- 139950898901232&#45;&gt;139950899021808 -->\n<g class=\"edge\" id=\"edge2\">\n<title>139950898901232-&gt;139950899021808</title>\n<path d=\"M532,-664.3799C532,-656.1745 532,-646.7679 532,-637.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"535.5001,-637.784 532,-627.784 528.5001,-637.784 535.5001,-637.784\" stroke=\"#000000\"/>\n</g>\n<!-- 139950899326088 -->\n<g class=\"node\" id=\"node5\">\n<title>139950899326088</title>\n<polygon fill=\"none\" points=\"56,-498.5 56,-544.5 346,-544.5 346,-498.5 56,-498.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"128.5\" y=\"-517.8\">FlattenMovies: Flatten</text>\n<polyline fill=\"none\" points=\"201,-498.5 201,-544.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"230\" y=\"-529.3\">input:</text>\n<polyline fill=\"none\" points=\"201,-521.5 259,-521.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"230\" y=\"-506.3\">output:</text>\n<polyline fill=\"none\" points=\"259,-498.5 259,-544.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302.5\" y=\"-529.3\">(None, 1, 8)</text>\n<polyline fill=\"none\" points=\"259,-521.5 346,-521.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"302.5\" y=\"-506.3\">(None, 8)</text>\n</g>\n<!-- 139950899141376&#45;&gt;139950899326088 -->\n<g class=\"edge\" id=\"edge3\">\n<title>139950899141376-&gt;139950899326088</title>\n<path d=\"M180.7996,-581.3799C183.6278,-572.9962 186.8791,-563.3584 189.9351,-554.2996\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"193.265,-555.3782 193.1452,-544.784 186.6322,-553.1406 193.265,-555.3782\" stroke=\"#000000\"/>\n</g>\n<!-- 139950898901288 -->\n<g class=\"node\" id=\"node6\">\n<title>139950898901288</title>\n<polygon fill=\"none\" points=\"378.5,-498.5 378.5,-544.5 657.5,-544.5 657.5,-498.5 378.5,-498.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"445.5\" y=\"-517.8\">FlattenUsers: Flatten</text>\n<polyline fill=\"none\" points=\"512.5,-498.5 512.5,-544.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"541.5\" y=\"-529.3\">input:</text>\n<polyline fill=\"none\" points=\"512.5,-521.5 570.5,-521.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"541.5\" y=\"-506.3\">output:</text>\n<polyline fill=\"none\" points=\"570.5,-498.5 570.5,-544.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"614\" y=\"-529.3\">(None, 1, 5)</text>\n<polyline fill=\"none\" points=\"570.5,-521.5 657.5,-521.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"614\" y=\"-506.3\">(None, 5)</text>\n</g>\n<!-- 139950899021808&#45;&gt;139950898901288 -->\n<g class=\"edge\" id=\"edge4\">\n<title>139950899021808-&gt;139950898901288</title>\n<path d=\"M528.1002,-581.3799C526.7162,-573.1745 525.1295,-563.7679 523.6301,-554.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"527.042,-554.0625 521.9274,-544.784 520.1395,-555.2269 527.042,-554.0625\" stroke=\"#000000\"/>\n</g>\n<!-- 139950899494080 -->\n<g class=\"node\" id=\"node7\">\n<title>139950899494080</title>\n<polygon fill=\"none\" points=\"72.5,-415.5 72.5,-461.5 343.5,-461.5 343.5,-415.5 72.5,-415.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"143\" y=\"-434.8\">dropout_15: Dropout</text>\n<polyline fill=\"none\" points=\"213.5,-415.5 213.5,-461.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"242.5\" y=\"-446.3\">input:</text>\n<polyline fill=\"none\" points=\"213.5,-438.5 271.5,-438.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"242.5\" y=\"-423.3\">output:</text>\n<polyline fill=\"none\" points=\"271.5,-415.5 271.5,-461.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"307.5\" y=\"-446.3\">(None, 8)</text>\n<polyline fill=\"none\" points=\"271.5,-438.5 343.5,-438.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"307.5\" y=\"-423.3\">(None, 8)</text>\n</g>\n<!-- 139950899326088&#45;&gt;139950899494080 -->\n<g class=\"edge\" id=\"edge5\">\n<title>139950899326088-&gt;139950899494080</title>\n<path d=\"M202.9499,-498.3799C203.6419,-490.1745 204.4352,-480.7679 205.1849,-471.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"208.6834,-472.0428 206.0363,-461.784 201.7082,-471.4545 208.6834,-472.0428\" stroke=\"#000000\"/>\n</g>\n<!-- 139950898901400 -->\n<g class=\"node\" id=\"node8\">\n<title>139950898901400</title>\n<polygon fill=\"none\" points=\"371.5,-415.5 371.5,-461.5 642.5,-461.5 642.5,-415.5 371.5,-415.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"442\" y=\"-434.8\">dropout_16: Dropout</text>\n<polyline fill=\"none\" points=\"512.5,-415.5 512.5,-461.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"541.5\" y=\"-446.3\">input:</text>\n<polyline fill=\"none\" points=\"512.5,-438.5 570.5,-438.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"541.5\" y=\"-423.3\">output:</text>\n<polyline fill=\"none\" points=\"570.5,-415.5 570.5,-461.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"606.5\" y=\"-446.3\">(None, 5)</text>\n<polyline fill=\"none\" points=\"570.5,-438.5 642.5,-438.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"606.5\" y=\"-423.3\">(None, 5)</text>\n</g>\n<!-- 139950898901288&#45;&gt;139950898901400 -->\n<g class=\"edge\" id=\"edge6\">\n<title>139950898901288-&gt;139950898901400</title>\n<path d=\"M514.9359,-498.3799C513.8484,-490.1745 512.6018,-480.7679 511.4237,-471.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"514.8694,-471.2375 510.0858,-461.784 507.9301,-472.1572 514.8694,-471.2375\" stroke=\"#000000\"/>\n</g>\n<!-- 139950899022312 -->\n<g class=\"node\" id=\"node9\">\n<title>139950899022312</title>\n<polygon fill=\"none\" points=\"184,-332.5 184,-378.5 520,-378.5 520,-332.5 184,-332.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"251\" y=\"-351.8\">Concat: Concatenate</text>\n<polyline fill=\"none\" points=\"318,-332.5 318,-378.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"347\" y=\"-363.3\">input:</text>\n<polyline fill=\"none\" points=\"318,-355.5 376,-355.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"347\" y=\"-340.3\">output:</text>\n<polyline fill=\"none\" points=\"376,-332.5 376,-378.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"448\" y=\"-363.3\">[(None, 8), (None, 5)]</text>\n<polyline fill=\"none\" points=\"376,-355.5 520,-355.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"448\" y=\"-340.3\">(None, 13)</text>\n</g>\n<!-- 139950899494080&#45;&gt;139950899022312 -->\n<g class=\"edge\" id=\"edge7\">\n<title>139950899494080-&gt;139950899022312</title>\n<path d=\"M248.112,-415.3799C265.0492,-405.6175 284.9366,-394.1546 302.7524,-383.8857\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"304.9375,-386.6661 311.8536,-378.6399 301.4419,-380.6014 304.9375,-386.6661\" stroke=\"#000000\"/>\n</g>\n<!-- 139950898901400&#45;&gt;139950899022312 -->\n<g class=\"edge\" id=\"edge8\">\n<title>139950898901400-&gt;139950899022312</title>\n<path d=\"M463.8239,-415.3799C445.342,-405.4832 423.5966,-393.8388 404.2191,-383.4625\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"405.6811,-380.2752 395.2132,-378.6399 402.3766,-386.4461 405.6811,-380.2752\" stroke=\"#000000\"/>\n</g>\n<!-- 139950898352816 -->\n<g class=\"node\" id=\"node10\">\n<title>139950898352816</title>\n<polygon fill=\"none\" points=\"197.5,-249.5 197.5,-295.5 506.5,-295.5 506.5,-249.5 197.5,-249.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"279.5\" y=\"-268.8\">FullyConnected-1: Dense</text>\n<polyline fill=\"none\" points=\"361.5,-249.5 361.5,-295.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"390.5\" y=\"-280.3\">input:</text>\n<polyline fill=\"none\" points=\"361.5,-272.5 419.5,-272.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"390.5\" y=\"-257.3\">output:</text>\n<polyline fill=\"none\" points=\"419.5,-249.5 419.5,-295.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"463\" y=\"-280.3\">(None, 13)</text>\n<polyline fill=\"none\" points=\"419.5,-272.5 506.5,-272.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"463\" y=\"-257.3\">(None, 100)</text>\n</g>\n<!-- 139950899022312&#45;&gt;139950898352816 -->\n<g class=\"edge\" id=\"edge9\">\n<title>139950899022312-&gt;139950898352816</title>\n<path d=\"M352,-332.3799C352,-324.1745 352,-314.7679 352,-305.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"355.5001,-305.784 352,-295.784 348.5001,-305.784 355.5001,-305.784\" stroke=\"#000000\"/>\n</g>\n<!-- 139950897754464 -->\n<g class=\"node\" id=\"node11\">\n<title>139950897754464</title>\n<polygon fill=\"none\" points=\"197.5,-166.5 197.5,-212.5 506.5,-212.5 506.5,-166.5 197.5,-166.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"279.5\" y=\"-185.8\">FullyConnected-2: Dense</text>\n<polyline fill=\"none\" points=\"361.5,-166.5 361.5,-212.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"390.5\" y=\"-197.3\">input:</text>\n<polyline fill=\"none\" points=\"361.5,-189.5 419.5,-189.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"390.5\" y=\"-174.3\">output:</text>\n<polyline fill=\"none\" points=\"419.5,-166.5 419.5,-212.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"463\" y=\"-197.3\">(None, 100)</text>\n<polyline fill=\"none\" points=\"419.5,-189.5 506.5,-189.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"463\" y=\"-174.3\">(None, 50)</text>\n</g>\n<!-- 139950898352816&#45;&gt;139950897754464 -->\n<g class=\"edge\" id=\"edge10\">\n<title>139950898352816-&gt;139950897754464</title>\n<path d=\"M352,-249.3799C352,-241.1745 352,-231.7679 352,-222.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"355.5001,-222.784 352,-212.784 348.5001,-222.784 355.5001,-222.784\" stroke=\"#000000\"/>\n</g>\n<!-- 139950897541528 -->\n<g class=\"node\" id=\"node12\">\n<title>139950897541528</title>\n<polygon fill=\"none\" points=\"201,-83.5 201,-129.5 503,-129.5 503,-83.5 201,-83.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"283\" y=\"-102.8\">FullyConnected-3: Dense</text>\n<polyline fill=\"none\" points=\"365,-83.5 365,-129.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"394\" y=\"-114.3\">input:</text>\n<polyline fill=\"none\" points=\"365,-106.5 423,-106.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"394\" y=\"-91.3\">output:</text>\n<polyline fill=\"none\" points=\"423,-83.5 423,-129.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"463\" y=\"-114.3\">(None, 50)</text>\n<polyline fill=\"none\" points=\"423,-106.5 503,-106.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"463\" y=\"-91.3\">(None, 20)</text>\n</g>\n<!-- 139950897754464&#45;&gt;139950897541528 -->\n<g class=\"edge\" id=\"edge11\">\n<title>139950897754464-&gt;139950897541528</title>\n<path d=\"M352,-166.3799C352,-158.1745 352,-148.7679 352,-139.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"355.5001,-139.784 352,-129.784 348.5001,-139.784 355.5001,-139.784\" stroke=\"#000000\"/>\n</g>\n<!-- 139950897541248 -->\n<g class=\"node\" id=\"node13\">\n<title>139950897541248</title>\n<polygon fill=\"none\" points=\"223.5,-.5 223.5,-46.5 480.5,-46.5 480.5,-.5 223.5,-.5\" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"283\" y=\"-19.8\">Activation: Dense</text>\n<polyline fill=\"none\" points=\"342.5,-.5 342.5,-46.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"371.5\" y=\"-31.3\">input:</text>\n<polyline fill=\"none\" points=\"342.5,-23.5 400.5,-23.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"371.5\" y=\"-8.3\">output:</text>\n<polyline fill=\"none\" points=\"400.5,-.5 400.5,-46.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"440.5\" y=\"-31.3\">(None, 20)</text>\n<polyline fill=\"none\" points=\"400.5,-23.5 480.5,-23.5 \" stroke=\"#000000\"/>\n<text fill=\"#000000\" font-family=\"Times,serif\" font-size=\"14.00\" text-anchor=\"middle\" x=\"440.5\" y=\"-8.3\">(None, 1)</text>\n</g>\n<!-- 139950897541528&#45;&gt;139950897541248 -->\n<g class=\"edge\" id=\"edge12\">\n<title>139950897541528-&gt;139950897541248</title>\n<path d=\"M352,-83.3799C352,-75.1745 352,-65.7679 352,-56.8786\" fill=\"none\" stroke=\"#000000\"/>\n<polygon fill=\"#000000\" points=\"355.5001,-56.784 352,-46.784 348.5001,-56.784 355.5001,-56.784\" stroke=\"#000000\"/>\n</g>\n</g>\n</svg>"
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 62
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "85pVufmtKduk",
        "colab_type": "code",
        "outputId": "a0ea319c-bfb5-46f9-f5e7-0f68c448c474",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        }
      },
      "source": [
        "history = model.fit([train.user_id, train.movie_id], train.rating, epochs=50, \n",
        "                    validation_split=0.2, verbose=1)"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Train on 64000 samples, validate on 16000 samples\n",
            "Epoch 1/50\n",
            "64000/64000 [==============================] - 10s 152us/step - loss: 1.1961 - val_loss: 0.9325\n",
            "Epoch 2/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.8980 - val_loss: 0.9178\n",
            "Epoch 3/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.8873 - val_loss: 0.9195\n",
            "Epoch 4/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.8769 - val_loss: 0.9051\n",
            "Epoch 5/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.8739 - val_loss: 0.9064\n",
            "Epoch 6/50\n",
            "64000/64000 [==============================] - 9s 134us/step - loss: 0.8700 - val_loss: 0.9048\n",
            "Epoch 7/50\n",
            "64000/64000 [==============================] - 9s 134us/step - loss: 0.8658 - val_loss: 0.9081\n",
            "Epoch 8/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.8572 - val_loss: 0.8882\n",
            "Epoch 9/50\n",
            "64000/64000 [==============================] - 9s 134us/step - loss: 0.8413 - val_loss: 0.8895\n",
            "Epoch 10/50\n",
            "64000/64000 [==============================] - 9s 138us/step - loss: 0.8278 - val_loss: 0.9243\n",
            "Epoch 11/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.8206 - val_loss: 0.9099\n",
            "Epoch 12/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.8115 - val_loss: 0.8857\n",
            "Epoch 13/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.8055 - val_loss: 0.8894\n",
            "Epoch 14/50\n",
            "64000/64000 [==============================] - 9s 134us/step - loss: 0.7986 - val_loss: 0.9195\n",
            "Epoch 15/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.7944 - val_loss: 0.8960\n",
            "Epoch 16/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.7884 - val_loss: 0.8867\n",
            "Epoch 17/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.7840 - val_loss: 0.8825\n",
            "Epoch 18/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.7811 - val_loss: 0.8875\n",
            "Epoch 19/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.7755 - val_loss: 0.8916\n",
            "Epoch 20/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.7734 - val_loss: 0.8894\n",
            "Epoch 21/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.7724 - val_loss: 0.9018\n",
            "Epoch 22/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.7691 - val_loss: 0.8986\n",
            "Epoch 23/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.7649 - val_loss: 0.8995\n",
            "Epoch 24/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.7646 - val_loss: 0.8870\n",
            "Epoch 25/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.7612 - val_loss: 0.8882\n",
            "Epoch 26/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.7594 - val_loss: 0.8875\n",
            "Epoch 27/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.7532 - val_loss: 0.9010\n",
            "Epoch 28/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.7501 - val_loss: 0.8990\n",
            "Epoch 29/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.7440 - val_loss: 0.8910\n",
            "Epoch 30/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.7389 - val_loss: 0.9016\n",
            "Epoch 31/50\n",
            "64000/64000 [==============================] - 8s 131us/step - loss: 0.7346 - val_loss: 0.8957\n",
            "Epoch 32/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.7307 - val_loss: 0.9006\n",
            "Epoch 33/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.7265 - val_loss: 0.8996\n",
            "Epoch 34/50\n",
            "64000/64000 [==============================] - 8s 131us/step - loss: 0.7210 - val_loss: 0.9049\n",
            "Epoch 35/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.7176 - val_loss: 0.9064\n",
            "Epoch 36/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.7145 - val_loss: 0.9037\n",
            "Epoch 37/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.7125 - val_loss: 0.8963\n",
            "Epoch 38/50\n",
            "64000/64000 [==============================] - 8s 131us/step - loss: 0.7089 - val_loss: 0.9166\n",
            "Epoch 39/50\n",
            "64000/64000 [==============================] - 8s 131us/step - loss: 0.7075 - val_loss: 0.9090\n",
            "Epoch 40/50\n",
            "64000/64000 [==============================] - 8s 131us/step - loss: 0.7046 - val_loss: 0.9112\n",
            "Epoch 41/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.7019 - val_loss: 0.9215\n",
            "Epoch 42/50\n",
            "64000/64000 [==============================] - 8s 133us/step - loss: 0.6984 - val_loss: 0.9064\n",
            "Epoch 43/50\n",
            "64000/64000 [==============================] - 9s 134us/step - loss: 0.6954 - val_loss: 0.9119\n",
            "Epoch 44/50\n",
            "64000/64000 [==============================] - 8s 132us/step - loss: 0.6935 - val_loss: 0.9133\n",
            "Epoch 45/50\n",
            "64000/64000 [==============================] - 8s 130us/step - loss: 0.6913 - val_loss: 0.9222\n",
            "Epoch 46/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.6861 - val_loss: 0.9281\n",
            "Epoch 47/50\n",
            "64000/64000 [==============================] - 9s 133us/step - loss: 0.6866 - val_loss: 0.9187\n",
            "Epoch 48/50\n",
            "64000/64000 [==============================] - 8s 129us/step - loss: 0.6809 - val_loss: 0.9254\n",
            "Epoch 49/50\n",
            "64000/64000 [==============================] - 8s 129us/step - loss: 0.6810 - val_loss: 0.9290\n",
            "Epoch 50/50\n",
            "64000/64000 [==============================] - 8s 130us/step - loss: 0.6772 - val_loss: 0.9425\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "QuX4v1LXKkpd",
        "colab_type": "code",
        "outputId": "c3d124ac-2abb-46d5-f7cf-2dc78dcb264d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 332
        }
      },
      "source": [
        "#train loss vs. epochs \n",
        "pd.Series(history.history['loss']).plot(logy=True, label = \"Train Loss\")\n",
        "pd.Series(history.history['val_loss']).plot(logy=True, color='green', label = \"Val Loss\")\n",
        "\n",
        "plt.title(\"Train Loss vs Val Loss - with Dropo regularization\")\n",
        "plt.xlabel(\"Epoch\")\n",
        "plt.ylabel(\"Loss (Log)\")\n",
        "plt.legend()\n",
        "\n",
        "y_hat = np.round(model.predict([test.user_id, test.movie_id]),0)\n",
        "y_true = test.rating\n",
        "\n",
        "from sklearn.metrics import mean_absolute_error\n",
        "mean_absolute_error(y_true, y_hat)\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "0.7235"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 67
        },
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 432x288 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "vKPLCCStaGZ6",
        "colab_type": "code",
        "outputId": "42d26464-775d-4d5b-a702-21bb27d7399d",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 138
        }
      },
      "source": [
        "#Matedata Exploration\n",
        "\n",
        "# users_cols = ['user_id', 'age', 'sex', 'occupation', 'zip_code']\n",
        "userinfo = pd.read_csv(\n",
        "    'ml-100k/u.info', sep='', names=users_cols, encoding='latin-1')\n",
        "userinfo\n"
      ],
      "execution_count": 0,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/html": [
              "<div>\n",
              "<style scoped>\n",
              "    .dataframe tbody tr th:only-of-type {\n",
              "        vertical-align: middle;\n",
              "    }\n",
              "\n",
              "    .dataframe tbody tr th {\n",
              "        vertical-align: top;\n",
              "    }\n",
              "\n",
              "    .dataframe thead th {\n",
              "        text-align: right;\n",
              "    }\n",
              "</style>\n",
              "<table border=\"1\" class=\"dataframe\">\n",
              "  <thead>\n",
              "    <tr style=\"text-align: right;\">\n",
              "      <th></th>\n",
              "      <th>user_id</th>\n",
              "      <th>age</th>\n",
              "      <th>sex</th>\n",
              "      <th>occupation</th>\n",
              "      <th>zip_code</th>\n",
              "    </tr>\n",
              "  </thead>\n",
              "  <tbody>\n",
              "    <tr>\n",
              "      <th>0</th>\n",
              "      <td>943 users</td>\n",
              "      <td>NaN</td>\n",
              "      <td>NaN</td>\n",
              "      <td>NaN</td>\n",
              "      <td>NaN</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>1</th>\n",
              "      <td>1682 items</td>\n",
              "      <td>NaN</td>\n",
              "      <td>NaN</td>\n",
              "      <td>NaN</td>\n",
              "      <td>NaN</td>\n",
              "    </tr>\n",
              "    <tr>\n",
              "      <th>2</th>\n",
              "      <td>100000 ratings</td>\n",
              "      <td>NaN</td>\n",
              "      <td>NaN</td>\n",
              "      <td>NaN</td>\n",
              "      <td>NaN</td>\n",
              "    </tr>\n",
              "  </tbody>\n",
              "</table>\n",
              "</div>"
            ],
            "text/plain": [
              "          user_id  age  sex  occupation  zip_code\n",
              "0       943 users  NaN  NaN         NaN       NaN\n",
              "1      1682 items  NaN  NaN         NaN       NaN\n",
              "2  100000 ratings  NaN  NaN         NaN       NaN"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 13
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "cV_cwsmNX5Xf",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        "# Reading ratings file\n",
        "ratings = pd.read_csv('ratings.csv', sep='\\t', encoding='latin-1', \n",
        "                      usecols=['user_id', 'movie_id', 'user_emb_id', 'movie_emb_id', 'rating'])\n",
        "max_userid = ratings['user_id'].drop_duplicates().max()\n",
        "max_movieid = ratings['movie_id'].drop_duplicates().max()\n",
        "\n",
        "# Reading ratings file\n",
        "users = pd.read_csv('users.csv', sep='\\t', encoding='latin-1', \n",
        "                    usecols=['user_id', 'gender', 'zipcode', 'age_desc', 'occ_desc'])\n",
        "\n",
        "# Reading ratings file\n",
        "movies = pd.read_csv('movies.csv', sep='\\t', encoding='latin-1', \n",
        "                     usecols=['movie_id', 'title', 'genres'])\n"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "SWaQnQOvXqj3",
        "colab_type": "code",
        "colab": {}
      },
      "source": [
        ".# Create training set\n",
        "shuffled_ratings = ratings.sample(frac=1., random_state=42)\n",
        "\n",
        "# Shuffling users\n",
        "Users = shuffled_ratings['user_emb_id'].values\n",
        "print ('Users:', Users, ', shape =', Users.shape)\n",
        "\n",
        "# Shuffling movies\n",
        "Movies = shuffled_ratings['movie_emb_id'].values\n",
        "print ('Movies:', Movies, ', shape =', Movies.shape)\n",
        "\n",
        "# Shuffling ratings\n",
        "Ratings = shuffled_ratings['rating'].values\n",
        "print ('Ratings:', Ratings, ', shape =', Ratings.shape)\n"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}
